// ****************************************************************
// Copyright 2012, Stephan Burguchev
// e-mail: me@sburg.net
// ****************************************************************
// *
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;

namespace Updater.Utils
{
    public static class EmbeddedDllLoader
    {
        private const string PlatformDir = "platform";
        private const string X64Dir = "x64";
        private const string X86Dir = "x86";

        private static readonly Dictionary<string, Assembly> _cache = new Dictionary<string, Assembly>();
        private static bool _isEnabled;

        /// <summary>
        ///     Gets or sets a value indicating whether this <see cref="EmbeddedDllLoader" />
        ///     will attempt to load missing assemblies.
        /// </summary>
        /// <value>
        ///     <c>true</c> if the missing assembly resolver is enabled; otherwise, <c>false</c>.
        /// </value>
        public static bool Enable
        {
            get
            {
                return _isEnabled;
            }
            set
            {
                lock (typeof(MultiPlatformDllLoader))
                {
                    if (_isEnabled != value)
                    {
                        if (value)
                        {
                            AppDomain.CurrentDomain.AssemblyResolve += resolver;
                        }
                        else
                        {
                            AppDomain.CurrentDomain.AssemblyResolve -= resolver;
                        }
                        _isEnabled = value;
                    }
                }
            }
        }

        public static string Namespace { get; set; }

        public static Assembly Source { get; set; }

        private static Assembly loadFromResource(string resourceName)
        {
            if (Source.GetManifestResourceNames().Contains(resourceName))
            {
                using (var ms = new MemoryStream())
                {
                    Source.GetManifestResourceStream(resourceName).CopyTo(ms);
                    return Assembly.Load(ms.ToArray());
                }
            }

            return null;
        }

        private static Assembly resolver(object sender, ResolveEventArgs args)
        {
            if (Source == null || Namespace == null)
            {
                throw new InvalidOperationException("The loader is not initialized.");
            }

            string assemblyName = args.Name.Split(new[] { ',' }, 2)[0];

            Assembly assembly;
            if (_cache.TryGetValue(assemblyName, out assembly))
            {
                return assembly;
            }

            string dllResource = Namespace + "." + assemblyName + ".dll";
            string exeResource = Namespace + "." + assemblyName + ".exe";

            assembly = loadFromResource(dllResource) ?? loadFromResource(exeResource);
            if (assembly != null)
            {
                _cache[assemblyName] = assembly;
            }

            return assembly;
        }
    }
}
